﻿/* 计算字符串或者文件的哈希值
 * 支持2种算法：MD5、SHA1
 */

/*待测试*/
namespace LSLib.Security
{
    using LSLib.StringEx;
    using System;
    using System.IO;
    using System.Security.Cryptography;
    using System.Text;

    public enum AlgName
    {
        md5,
        sha1,
    }

    public interface ICalcHashData
    {
        AlgName algName { get; }
        string CalcString(string input);
        string CalcFile(string fileName);
    }

    public static class CalcHashDataHelper
    {
        /// <summary>计算哈希值
        /// </summary>
        /// <param name="input">要计算哈希值的 字符串</param>
        /// <param name="algName">算法:sha1,md5</param>
        /// <returns>哈希值字节数组</returns>
        public static byte[] CalcHashData(string input, AlgName algName)
        {
            using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(input)))
            {
                return CalcHashData(ms, algName);
            }
        }
        /// <summary>计算哈希值
        /// </summary>
        /// <param name="stream">要计算哈希值的 流</param>
        /// <param name="algName">算法:sha1,md5</param>
        /// <returns>哈希值字节数组</returns>
        public static byte[] CalcHashData(Stream stream, AlgName algName)
        {
            HashAlgorithm algorithm;
            switch (algName)
            {
                case AlgName.md5:
                    algorithm = MD5.Create();
                    break;
                case AlgName.sha1:
                    algorithm = SHA1.Create();
                    break;
                default:
                    throw new ArgumentException("只能使用 sha1 或 md5 算法！", "algName");
            }
            return algorithm.ComputeHash(stream);
        }

        /// <summary> 计算字符串的哈希值
        /// </summary>
        /// <param name="input">要计算哈希值的字符串</param>
        /// <param name="algName">算法:sha1,md5</param>
        /// <returns>哈希值16进制字符串</returns>
        public static string CalcHashString(string input, AlgName algName)
        {
            if (string.IsNullOrEmpty(input)) return string.Empty;

            byte[] hashBytes = CalcHashData(input, algName);
            return HexString.Bytes2HexString(hashBytes);
        }

        /// <summary>计算文件的哈希值
        /// </summary>
        /// <param name="fileName">要计算哈希值的文件名和路径</param>
        /// <param name="algName">算法:sha1,md5</param>
        /// <returns>哈希值16进制字符串</returns>
        public static string CalcHashFile(string fileName, AlgName algName)
        {
            if (File.Exists(fileName) == false) throw new FileNotFoundException("文件不存在！", fileName);

            using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
            {
                byte[] hashBytes = CalcHashData(fs, algName);
                return HexString.Bytes2HexString(hashBytes);
            }
        }
    }

    public class CalcHashData_MD5 : ICalcHashData
    {
        public AlgName algName
        {
            get { return AlgName.md5; }
        }

        /// <summary>计算字符串的 MD5 值
        /// </summary>
        /// <param name="input">要计算 MD5 值的字符串</param>
        /// <returns>MD5 值16进制字符串</returns>
        public string CalcString(string input)
        {
            return CalcHashDataHelper.CalcHashString(input, algName);
        }

        /// <summary>计算文件的 MD5 值
        /// </summary>
        /// <param name="fileName">要计算 MD5 值的文件名和路径</param>
        /// <returns>MD5 值16进制字符串</returns>
        public string CalcFile(string fileName)
        {
            return CalcHashDataHelper.CalcHashFile(fileName, algName);
        }
    }

    public class CalcHashData_SHA1 : ICalcHashData
    {
        public AlgName algName
        {
            get { return AlgName.sha1; }
        }

        /// <summary>计算字符串的 SHA1 值
        /// </summary>
        /// <param name="input">要计算 SHA1 值的字符串</param>
        /// <returns>sha1 值16进制字符串</returns>
        public string CalcString(string input)
        {
            return CalcHashDataHelper.CalcHashString(input, algName);
        }

        /// <summary>计算文件的 SHA1 值
        /// </summary>
        /// <param name="fileName">要计算 sha1 值的文件名和路径</param>
        /// <returns>sha1 值16进制字符串</returns>
        public string CalcFile(string fileName)
        {
            return CalcHashDataHelper.CalcHashFile(fileName, algName);
        }
    }
}
